package jsen.tool.ddd.demo.domain.parking;

import jsen.tool.ddd.adapter.event.Consumer;
import jsen.tool.ddd.adapter.event.IEventBus;
import jsen.tool.ddd.adapter.infrastructure.specification.ISpecification;
import jsen.tool.ddd.adapter.infrastructure.specification.base.SpecContext;
import jsen.tool.ddd.demo.domain.base.DomainObject;
import jsen.tool.ddd.demo.domain.base.Ds;
import jsen.tool.ddd.demo.domain.parking.event.ParkingStatusChange;
import jsen.tool.ddd.demo.domain.parking.spec.parking.ParkingLotSpec;
import jsen.tool.ddd.domain.AggregateObject;
import lombok.Builder;
import lombok.Getter;

/**
 * @author jsen
 * @date 2021/10/6
 */
@AggregateObject(name = "ParkingLot", desc = "停车场系统")
@Builder
@Getter
public class ParkingLot implements DomainObject {
    private final ISpecification<ParkingLot> strict = new ParkingLotSpec();

    private final ParkingLotMonitor monitor;
    private ParkingLotStatus parkingLotStatus;

    public void startParkingLot(IEventBus bus) {
        class ChangeConsumer implements Consumer<ParkingStatusChange> {
            @Override
            public void accept(ParkingStatusChange t) {
                refreshStatus(parkingLotStatus.parkingCntChange(t.getFreeLocationCnt(), t.getBusyLocationCnt()));
            }
        }
        bus.register(new ChangeConsumer());
        monitor.startMonitor(bus);
    }

    private void refreshStatus(ParkingLotStatus status) {
        this.parkingLotStatus = status;
    }

    public ParkingLotStatus currentStatus() {
        return parkingLotStatus;
    }

    public void carIn(Car car) {
        SpecContext<Car> valid = car.strict(null);
        if (!valid.isValid()) {
            throw new IllegalStateException(valid.getSpecError().toString());
        }
        if (parkingLotStatus.isFull()) {
            throw new UnsupportedOperationException("容量已满");
        }
        boolean in = monitor.carIn(car);
        if (in) {
            Ds.addPlate(car.getPlate());
            refreshStatus(parkingLotStatus.carCntChange(parkingLotStatus.getCarCnt() + 1));
        }
    }

    public void carOut(Car car) {
        boolean out = monitor.carOut(car);
        if (out) {
            Ds.removePlate(car.getPlate());
            refreshStatus(parkingLotStatus.carCntChange(parkingLotStatus.getCarCnt() - 1));
        }
    }

}
